/** @file    NetworkChannelTcp.cpp
 *  @author  Alessandro Polo
 *  @version $Id: NetworkChannelTcp.cpp 2829 2010-08-02 20:45:47Z alex $
 *  @brief
 * File containing methods for the wosh::network::NetworkChannelTcp class.
 * The header for this class can be found in NetworkChannelTcp.h, check that file
 * for class description.
 ****************************************************************************/
/* Copyright (c) 2007-2010, WOSH - Wide Open Smart Home 
 * by Alessandro Polo - OpenSmartHome.com
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the OpenSmartHome.com WOSH nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Alessandro Polo ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Alessandro Polo BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 ****************************************************************************/

 #include <framework/network/NetworkChannelTcp.h>

 #include <core/ObjectFactory.h>

 #include <core/PersistenceManager.h>
 #include <core/WoshKernel.h>
 #include <core/NetworkManager.h>
 #include <stdio.h> //@bug BUG: TEMP TO BE REMOVED


using namespace std;
namespace wosh {
 namespace network {

 WOSH_REGISTER(wosh::network::NetworkChannelTcp, "wosh::NetworkChannel", 1.01, _static_NetworkChannelTcp)

#define _NetworkChannelTcp_PING_IDLE			0
#define _NetworkChannelTcp_PING_WAITING			1
#define _NetworkChannelTcp_PING_PONG			2

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS

NetworkChannelTcp::NetworkChannelTcp( const std::string& name )
	: wosh::ThreadImpl(NULL, "wosh.Network.NetworkChannelTcp"), wosh::NetworkChannel(name) {
	std::string nname = name;
	if ( name == "" )
		Object::setName("NetworkChannelTcp", this->getID());
	else
		Object::setName(name);
	Log.setContext(Object::getName());

	this->ping_state = _NetworkChannelTcp_PING_IDLE;

//Log.setLevel( LOG_VERBOSE );

	this->socket_id = 0;
	this->tcpSocket = NULL;
	this->handshaked = false;
	this->writing = false;

	Properties.set( _NetworkChannelTcp_KEY_Role, "", Permission::Read );
	Properties.set( _NetworkChannelTcp_KEY_BufferSize, _NetworkChannelTcp_BUFFER, Permission::Read );

	this->waitingPacketSize = 0;
	this->waitingBufferedSize = 0;
	this->msgBufferIn = new char[_NetworkChannelTcp_BUFFER];
	memset( this->msgBufferIn, '\0', _NetworkChannelTcp_BUFFER );
	this->msgBufferOut = new char[_NetworkChannelTcp_BUFFER];
	memset( this->msgBufferOut, '\0', _NetworkChannelTcp_BUFFER );
	this->msgBufferOutSize = 0;
 }

NetworkChannelTcp::~NetworkChannelTcp() {
	if ( isThreadRunning() )
		quitThread(60000);

	this->msgBufferOutMux.lockForWrite();
	if ( this->msgBufferOut != NULL )
		delete [] this->msgBufferOut;
	this->msgBufferOut = NULL;
	this->msgBufferOutSize = 0;
	this->msgBufferOutMux.unLock();

	this->msgBufferInMux.lockForWrite();
	if ( this->msgBufferIn != NULL )
		delete [] this->msgBufferIn;
	this->msgBufferIn = NULL;
	this->msgBufferInMux.unLock();
 }

//////////////////////////////////////////////////////////////////////////////////////////////// CONSTRUCTORS
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

bool NetworkChannelTcp::initializingThread() {
	std::string addressport = Properties.getValueOf( _NetworkChannel_KEY_ConnectAddress ).toString();
	if ( this->socket_id == 0 && addressport.size() == 0 ) {
		Log(LOG_CRITICAL, ":initializingThread() Invalid SocketDescriptor/TargetAddress.." );
		return false;
	 }
	this->tcpSocket = ObjectFactory::createTypeOf<wosh::network::SocketTcp>(false);
	if ( this->tcpSocket == NULL ) {
		Log(LOG_CRITICAL, ":initializingThread() FAILED Creating SocketTcp!" );
		return false;
	 }
	this->tcpSocket->setListener(this);
	this->handshaked = false;

	if ( this->socket_id != 0 ) {
		this->tcpSocket->setSocket(this->socket_id);
		Properties.transactionBeginWrite();
		Properties.update( _NetworkChannelTcp_KEY_Role, _NetworkChannelTcp_RoleServer );
		Properties.update( _NetworkChannel_KEY_ConnectAddress, this->tcpSocket->getAddressPort() );
//		Properties.update( _NetworkChannel_KEY_BindAddress, this->tcpSocket->getPeerAddressPort() );
		Properties.transactionEnd();
	 }
	else {
		this->tcpSocket->setAddressPort(addressport);
		Log(LOG_INFO, ":initializingThread() Connecting Socket to '%s'..", addressport.c_str() );

		WRESULT ret = this->tcpSocket->connect(120000);
		if ( WFAILED(ret) ) {
			Properties.transactionBeginWrite();
			Properties.update( _NetworkChannel_KEY_Connected, false );
			Properties.transactionEnd();
			Log(LOG_CRITICAL, ":initializingThread() FAILED#%d Connecting Socket to '%s'..", ret, addressport.c_str() );
			delete this->tcpSocket; this->tcpSocket = NULL;
			return false;
		 }
		Properties.transactionBeginWrite();
		Properties.update( _NetworkChannelTcp_KEY_Role, _NetworkChannelTcp_RoleClient );
		Properties.update( _NetworkChannel_KEY_BindAddress, "" );
		Properties.update( _NetworkChannel_KEY_ConnectAddress, addressport );
		Properties.transactionEnd();
		declarateWoshHost();
	 }
	QObject::connect(this, SIGNAL(writeData_s()), this, SLOT(writeData()), Qt::QueuedConnection );
	Properties.transactionBeginWrite();
	Properties.update( _NetworkChannel_KEY_Connected, true );
	Properties.transactionEnd();
	return true;
 }

void NetworkChannelTcp::runThread() {
	this->waitingBufferedSize = 0;
	this->writing = false;
	Log(LOG_VERBOSE, ":runThread() Entering QT Exec" );
	exec();
 }

void NetworkChannelTcp::exitingThread() {
	Properties.transactionBeginWrite();
	Properties.update( _NetworkChannel_KEY_Connected, false );
	Properties.transactionEnd();
	if ( this->listener != NULL )
		this->listener->channel_Disconnected(this, this->remoteWoshName);
	
	if ( this->tcpSocket->isConnected() ) {
		WRESULT ret = this->tcpSocket->disconnect();
		if ( WFAILED(ret) )
			Log(LOG_WARNING, ":exitingThread() FAILED#%d Disconnecting Socket..", ret );
	 }
	Log(LOG_VERBOSE, ":exitingThread() Destroying TCP Socket.." );
	WRESULT destroyed = ObjectFactory::destroy(this->tcpSocket);
	if ( WFAILED(destroyed) ) {
		Log(LOG_CRITICAL, ":exitingThread() FAILED#%d Destroying Socket", destroyed );
	 }
	this->tcpSocket = NULL;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void NetworkChannelTcp::tcpSocketError( int error_code, const char* description, wosh::network::SocketTcp* ) {
	if ( description == NULL )
		Log(LOG_WARNING, ":tcpSocketError(%d) Unknown Error description", error_code );
	else
		Log(LOG_WARNING, ":tcpSocketError(%d) : %s", error_code, description );
	switch(error_code) {
		case _SocketTcp_ERROR_RemoteHostClosedError: {
			break;
		 }
		default: {
			if ( this->listener != NULL )
				this->listener->channel_Error(this, error_code, "-" );
			break;
		 }
	 }
	if ( isThreadRunning() ) {
		Log(LOG_VERBOSE, ":tcpSocketError() : Thread is Running, Exiting.." );
		ThreadImpl::quitThread(0); // ASYNCH!
	 }
	else {
		Properties.transactionBeginWrite();
		Properties.update( _NetworkChannel_KEY_Connected, false );
		Properties.transactionEnd();
		if ( this->listener != NULL )
			this->listener->channel_Disconnected(this, this->remoteWoshName);

	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void NetworkChannelTcp::tcpDataAvailable( long data_len, wosh::network::SocketTcp* socket ) {
	if ( socket == NULL ) return;
	Log(LOG_VERBOSE, ":tcpDataAvailable(%ld) ..", data_len );
	data_len = _GET_Min(data_len, _NetworkChannelTcp_BUFFER);
	// parse chunk of data
	parseChunk(data_len, socket);
 }

void NetworkChannelTcp::parseChunk( long data_len, wosh::network::SocketTcp* socket ) {
	if ( socket == NULL ) return;
	// new packet from scratch?
	if ( this->waitingPacketSize == 0 ) {
		socket->read( (char*)&this->waitingPacketSize, sizeof(long) );
		this->waitingBufferedSize = 0;
		// starting to buffer data?
		if ( data_len > (long)sizeof(long) )
			parseChunk( data_len-sizeof(long), socket );
		return;
	 }
	if ( data_len == sizeof(long) ) { // buggy, what if these 2 bytes are missing from previous packet?
		socket->read( (char*)&this->waitingPacketSize, sizeof(long) );
		if ( this->waitingPacketSize > _NetworkChannelTcp_BUFFER ) {
			tcpSocketError(112, "Too large incoming packet!", NULL);
			this->waitingPacketSize = _NetworkChannelTcp_BUFFER - sizeof(long);
		 }
		this->waitingBufferedSize = 0;
		return;
	 }
	// from now: parsing more data (even more than 1 packet!)
	if ( this->waitingPacketSize == data_len ) {
		// great we got the whole message now
		data_len = socket->read(this->msgBufferIn, data_len );
		parseDataMessage(this->msgBufferIn, data_len);
		this->waitingPacketSize = 0;
		this->waitingBufferedSize = 0;
		return;
	 }
	// still waiting for more data?
	if ( this->waitingPacketSize > data_len+this->waitingBufferedSize ) {
		// current packet was splitted, just buffer it!
		data_len = socket->read(this->msgBufferIn+this->waitingBufferedSize, data_len );
		this->waitingBufferedSize += data_len;
		return;
	 }
	// shouldn't ever happen?
	if ( this->waitingPacketSize < this->waitingBufferedSize ) {
		Log(LOG_CRITICAL, ":parseChunk() UNHANDLED CONDITION TO CHECK, ITS A BUG.." );
		// get first (oldest) packet
		return;
	 }
	// got the full packet and nothing more
	if ( this->waitingPacketSize == data_len+this->waitingBufferedSize ) {
		socket->read(this->msgBufferIn+this->waitingBufferedSize, data_len );
		parseDataMessage(this->msgBufferIn, this->waitingPacketSize);
		this->waitingPacketSize = 0;
		this->waitingBufferedSize = 0;
		return;
	 }
	// got more data than expected
	if ( this->waitingPacketSize < data_len+this->waitingBufferedSize ) {
		// we have a new chuck of previous packet AND MORE data (even more packets)
		// new buffer* is (this->msgBufferIn+this->waitingBufferedSize)
		// data size of previous packet is (this->waitingPacketSize-this->waitingBufferedSize)
		long sizeRead = this->waitingPacketSize - this->waitingBufferedSize;
		socket->read(this->msgBufferIn+this->waitingBufferedSize, sizeRead );
		parseDataMessage(this->msgBufferIn, this->waitingPacketSize);

		// now process next packet, start at this->msgBufferIn+this->waitingPacketSize
		// so we STILL have (data_len-sizeRead) bytes ready to be read
		// we can reset the buffer and go on with next packet!
		this->waitingBufferedSize = 0;
		this->waitingPacketSize = 0;
		parseChunk( data_len-sizeRead, socket );
		return;
	 }
	Log(LOG_WARNING, ":parseChunk() I/O ERROR! Resetting" );
	this->waitingBufferedSize = 0;
	this->waitingPacketSize = 0;
	setReceivedNow( false );

	ping(10);
 }

bool NetworkChannelTcp::parseDataMessage( const char* data, long data_len ) {
	if ( data == NULL || data_len == 0 ) return false;
	doDumpInput(data, data_len);
	if ( strncmp(data, "HS_", 3) == 0 ) {
		WoshHost* remoteHost = new WoshHost();
		int64 ret = PersistenceManager::deserializeFrom( remoteHost, "XML", data+3, data_len-3);
		if ( ret <= 0 ) {
			Log(LOG_CRITICAL, ":tcpDataAvailable(%ld) WoshHost deserialization Size=%"PRId64, data_len, ret );
			delete remoteHost;
			return true;
		 }
		this->remoteWoshName = remoteHost->getName();
		Log(LOG_INFO, ":tcpDataAvailable(%ld) Got Handshake from %s..", data_len, this->remoteWoshName.c_str() );
		bool prev_handshaked = this->handshaked;
		this->handshaked = true;
		setSeenNow();
		if ( this->listener != NULL )
			this->listener->channel_Connected(this, *remoteHost);
		delete remoteHost;
		if ( !prev_handshaked ) {
			declarateWoshHost();
		 }
		return true;
	 }
	else if ( strncmp(data, "CTCP_PING_", 10) == 0 ) {
		got_ping();
		return true;
	 }
	else if ( strncmp(data, "CTCP_PONG_", 10) == 0 ) {
		got_pong();
		return true;
	 }
	if ( this->listener == NULL ) {
		return true;
	 }
	wosh::Message* message = new wosh::Message();
	int64 ret = PersistenceManager::deserializeFrom( message, "XML", data, data_len);
	setReceivedNow( (ret > 0) );
//	printf("\nDeSerialized MESSAGE [%"PRId64"]:\n%s\n( DeSerialized MESSAGE [%"PRId64"] )\n", ret, data, message->getID());

	if ( ret <= 0 ) {
		Log(LOG_WARNING, ":tcpDataAvailable() Message::DeSerialization ERROR" );
		printf( "\nTEMP-DEBUG NetworkChannelTcp::parseDataMessage() size: %ld\n%s\n", data_len, data );
		delete message;
		return false;
	 }
	message->setSourceUUID( this->getEntityID() ); // cos of flooding
	this->listener->channel_incomingMessage( this, message ); // inject
	return true;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

void NetworkChannelTcp::writeData() {
	this->msgBufferOutMux.lockForRead();
	int size = this->msgBufferOutSize;
	if ( size == 0 ) {
		this->msgBufferOutMux.unLock();
		return;
	 }
	this->writing = true;

	this->tcpSocket->sendDatagram((char*)&size, sizeof(long));
	int bs = this->tcpSocket->sendDatagram(this->msgBufferOut, size);
	doDumpOutput();
	this->writing = false;
	this->msgBufferOutSize = 0;
	this->msgBufferOutMux.unLock();
	if ( bs != size ) {
		Log(LOG_WARNING, ":writeData() FAILED Sending data [size: %d, sent: %d]", size, bs );
		return;
	 }
	else
		Log(LOG_VERBOSE, ":writeData() sent data [%d bytes]", size );
 }

WRESULT NetworkChannelTcp::sendMessage( const wosh::Message* message ) {
	if ( message == NULL ) return WRET_ERR_PARAM;
	if ( !this->running ) return WRET_ERR_ILLEGAL_USE;
	if ( this->tcpSocket == NULL || this->msgBufferOut == NULL ) return WRET_ERR_INTERNAL;

	int i = (10*5) * 20;//sec
	while( this->writing && --i > 0 )
		ThreadImpl::sleepForMSec(20);
	if ( this->writing ) {
		Log(LOG_CRITICAL, ":sendMessage() STILL writing [timeout]!? [FORCED RESET]" );
		this->writing = false;
		return WRET_ERR_INTERNAL;
	 }

	this->msgBufferOutMux.lockForWrite();

	int64 size = PersistenceManager::serializeTo( message, "XML", this->msgBufferOut, _NetworkChannelTcp_BUFFER, true );
//	printf("\nSerialized MESSAGE [%"PRId64"]:\n%s\nSerialized MESSAGE [%"PRId64"]\n", size, this->msgBufferOut, message->getID());

	setTransmittedNow( (size > 0) );
	if ( size <= 0 ) {
		this->msgBufferOutSize = 0;
		this->msgBufferOutMux.unLock();
		Log(LOG_WARNING, ":sendMessage() Serialization ERROR, Size=%"PRId64, size );
		return WRET_ERR_INTERNAL;
	 }
	this->msgBufferOutSize = size;
	this->writing = true;
	this->msgBufferOutMux.unLock();

	emit writeData_s();

	return WRET_OK;
 }

WRESULT NetworkChannelTcp::sendData( const char* data, unsigned long size ) {
	if ( data == NULL ) return WRET_ERR_PARAM;
	if ( !this->running ) return WRET_ERR_ILLEGAL_USE;
	if ( this->tcpSocket == NULL || this->msgBufferOut == NULL ) return WRET_ERR_INTERNAL;

	int i = 50*2;
	while(this->writing && --i > 0)
		ThreadImpl::sleepForMSec(20);

	this->msgBufferOutMux.lockForWrite();
	memcpy(this->msgBufferOut, data, size);
	this->msgBufferOutSize = size;
	this->writing = true;
	this->msgBufferOutMux.unLock();

	emit writeData_s();

	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT NetworkChannelTcp::connect( const std::string& addressport, unsigned long timeout ) {
	Properties.transactionBeginWrite();
	Properties.update( _NetworkChannel_KEY_ConnectAddress, addressport );
	Properties.transactionEnd();
	return connect( timeout );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT NetworkChannelTcp::connect( unsigned long timeout ) {
	if ( isThreadRunning() ) {
		Log(LOG_CRITICAL, ":connect() Thread is running!" );
		return WRET_ERR_ILLEGAL_USE;
	 }
	if ( isConnected() ) {
		Log(LOG_CRITICAL, ":connect() Already connected!" );
		return WRET_ERR_ILLEGAL_USE;
	 }
	string address = Properties.getValueOf( _NetworkChannel_KEY_ConnectAddress ).toString();
	Log(LOG_INFO, ":connect() to %s [socket#%d]", address.c_str(), this->socket_id );
	WRESULT ret = startThread(timeout);
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":connect(%s [socket#%d],%lu) Listening FAILED#%d [TimedOut]!", address.c_str(), this->socket_id, timeout, ret );
	else {
		if ( timeout == 0 )
			Log(LOG_VERBOSE, ":connect(%s [socket#%d]) Asynch Listening..", address.c_str(), this->socket_id );
		else
			Log(LOG_INFO, ":connect(%s [socket#%d]) Listening succedded.", address.c_str(), this->socket_id );
	 }
	return ret;
 }

WRESULT NetworkChannelTcp::disconnect( unsigned long timeout ) {
	if ( !isThreadRunning() ) {
		Log(LOG_CRITICAL, ":disconnect() Thread is NOT running!" );
		return WRET_ERR_ILLEGAL_USE;
	 }
	Log(LOG_VERBOSE, ":disconnect(%lu) ", timeout );
	WRESULT ret = ThreadImpl::quitThread(timeout);
	if ( WFAILED(ret) )
		Log(LOG_CRITICAL, ":disconnect(%lu) FAILED#%d [TimedOut]!", timeout, ret );
	else {
		if ( timeout == 0 )
			Log(LOG_VERBOSE, ":disconnect() Asynch Disconnecting.." );
		else
			Log(LOG_INFO, ":disconnect() Disconnecting succedded." );
	 }
	return ret;
 }

bool NetworkChannelTcp::isConnected() const {
	return ( isThreadRunning() && this->tcpSocket != NULL && this->tcpSocket->isConnected() );
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT NetworkChannelTcp::declarateWoshHost() {
	if ( this->tcpSocket == NULL || this->msgBufferOut == NULL ) return WRET_ERR_INTERNAL;
	WoshHost* localhost = WoshKernel::getInstance()->network().getLocalWoshHost();
	this->msgBufferOutMux.lockForWrite();
	memcpy(this->msgBufferOut, "HS_", 3);

	int64 size = PersistenceManager::serializeTo( localhost, "XML", this->msgBufferOut+3, _NetworkChannelTcp_BUFFER, true );
	if ( size <= 0 ) {
		this->msgBufferOutSize = 0;
		this->msgBufferOutMux.unLock();
		Log(LOG_WARNING, ":declarateWoshHost() Serialization ERROR, Size=%"PRId64, size );
		delete localhost;
		return WRET_ERR_INTERNAL;
	 }
	this->msgBufferOutSize = size+3;
	this->writing = true;
	this->tcpSocket->sendDatagram((char*)&this->msgBufferOutSize, sizeof(long));
	this->tcpSocket->sendDatagram(this->msgBufferOut, this->msgBufferOutSize);
	doDumpOutput();
	this->writing = false;
	this->msgBufferOutSize = 0;
	this->msgBufferOutMux.unLock();
	delete localhost;
	Log(LOG_INFO, ":declarateWoshHost() Sent WoshHost [%"PRId64"+3 bytes]", size );
	return WRET_OK;
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

WRESULT NetworkChannelTcp::ping( unsigned long timeout ) {
	if ( this->tcpSocket == NULL || this->msgBufferOut == NULL ) return WRET_ERR_INTERNAL;
	Log(LOG_INFO, ":ping(%ld) Pinging..", timeout );
	this->msgBufferOutMux.lockForWrite();
	memcpy(this->msgBufferOut, "CTCP_PING_", 10);
	this->msgBufferOutSize = 10;
	this->msgBufferOutMux.unLock();
	this->ping_state = _NetworkChannelTcp_PING_WAITING;

	emit writeData_s();

	if ( timeout == 0 ) return WRET_OK;
	timeout = (unsigned long)(timeout / 100) + 1;
	unsigned long i = 0;
	while( ++i < timeout ) {
		if ( this->ping_state == _NetworkChannelTcp_PING_PONG ) {
			this->ping_state = _NetworkChannelTcp_PING_IDLE;
			return WRET_OK;
		 }
		ThreadImpl::sleepForMSec(100);
	 }
	this->ping_state = _NetworkChannelTcp_PING_IDLE;
	return WRET_ERR_INTERNAL;
 }

void NetworkChannelTcp::got_ping() {
	if ( this->tcpSocket == NULL || this->msgBufferOut == NULL ) return;
	setSeenNow();
	Log(LOG_INFO, ":tcpDataAvailable() Got PING! (Replying..)" );
	this->msgBufferOutMux.lockForWrite();
	memcpy(this->msgBufferOut, "CTCP_PONG_", 10);
	this->msgBufferOutSize = 10;
	this->tcpSocket->sendDatagram((char*)&this->msgBufferOutSize, sizeof(long));
	this->tcpSocket->sendDatagram(this->msgBufferOut, this->msgBufferOutSize);
	doDumpOutput();
	this->msgBufferOutSize = 0;
	this->msgBufferOutMux.unLock();
 }

void NetworkChannelTcp::got_pong() {
	if ( this->tcpSocket == NULL ) return;
	this->ping_state = _NetworkChannelTcp_PING_PONG;
	setSeenNow();
	Log(LOG_INFO, ":tcpDataAvailable() Got PONG!" );
 }

void NetworkChannelTcp::doDumpOutput() {
	if ( this->dumpOutput != NULL ) {
		fwrite( (char*)&this->msgBufferOutSize, 1, sizeof(long), this->dumpOutput );
		fwrite( this->msgBufferOut, 1, this->msgBufferOutSize, this->dumpOutput );
		fflush(this->dumpOutput);
	 }
 }

void NetworkChannelTcp::doDumpInput( const char* data, long data_len ) {
	if ( this->dumpInput != NULL ) {
		fwrite( data, 1, data_len, this->dumpInput );
		fflush(this->dumpInput);
	 }
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////

std::string NetworkChannelTcp::getProtocol() const {
	if ( this->tcpSocket == NULL ) return "TCP://";
	return "TCP://"+this->tcpSocket->getAddress();
 }

std::string NetworkChannelTcp::getConnectedAddressPort() const {
	if ( this->tcpSocket == NULL ) return "";
	return this->tcpSocket->getAddress();
 }

//////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////

 }; // namespace network
}; // namespace wosh
